using System;
using System.Reflection;
using System.Configuration;

namespace BizElements.BusinessLayer
{
    #region Documentation.
    /// <summary>Configurable utility/facade class that simplifies common operations involving remote method calls. Includes strongly typed generic method invocations.</summary>
    /// <remarks><para>The class must be setup at application startup or configured in a *.config file. All 
    /// textual (string) properties may be configured in the <b>appSettings</b> section of a *.config file. Example:
    /// <code>
    /// <add key="BizElements.BusinessLayer.RpcHelper.MethodRemoterAssembly" value="BizElements.BusinessLayer"/>
    /// <add key="BizElements.BusinessLayer.RpcHelper.MethodRemoterClass" value="BizElements.BusinessLayer.DirectMethodCaller"/>
    /// <add key="BizElements.BusinessLayer.RpcHelper.MethodRemoterUrl" value="http://localhost/MyWebServices/WsMethodReceiver.asmx"/>
    /// </code>
    /// </para></remarks>
    #endregion
    public static class RpcHelper
    {
        #region Configuration.

        static IMethodRemoter methodRemoter;
        static string methodRemoterAssembly = "BizElements.BusinessLayer";
        static string methodRemoterClass = "BizElements.BusinessLayer.DirectMethodCaller";
        static string methodRemoterUrl;

        static readonly string[] AssemblyAppSettingsKeys = new string[] { "BizElements.BusinessLayer.RpcHelper.MethodRemoterAssembly" };
        static readonly string[] ClassAppSettingsKeys = new string[] { "BizElements.BusinessLayer.RpcHelper.MethodRemoterClass" };
        static readonly string[] UrlAppSettingsKeys = new string[] { "BizElements.BusinessLayer.RpcHelper.MethodRemoterUrl" };


        static RpcHelper()
        {
            string cfgAssembly = GetAppSetting(AssemblyAppSettingsKeys);
            if (!string.IsNullOrEmpty(cfgAssembly))
                methodRemoterAssembly = cfgAssembly;

            string cfgClass = GetAppSetting(ClassAppSettingsKeys);
            if (!string.IsNullOrEmpty(cfgClass))
                methodRemoterClass = cfgClass;

            string cfgUrl = GetAppSetting(UrlAppSettingsKeys);
            if (!string.IsNullOrEmpty(cfgUrl))
                methodRemoterUrl = cfgUrl;

            methodRemoter = (IMethodRemoter)Assembly.Load(methodRemoterAssembly).CreateInstance(methodRemoterClass);
            if (!string.IsNullOrEmpty(methodRemoterUrl))
                methodRemoter.Url = methodRemoterUrl;
        }

        private static string GetAppSetting(string[] possibleKeys)
        {
            foreach (string key in possibleKeys)
            {
                string value = ConfigurationManager.AppSettings[key];
                if (!string.IsNullOrEmpty(value))
                    return value;
            }

            return null;
        }

        #endregion

        #region Remoter properties.

        /// <summary>
        /// Gets the configured object which remotes method invocations.
        /// </summary>
        /// <value>An instance of <see cref="IMethodRemoter"/>. Default is <see cref="DirectMethodCaller"/>.</value>
        public static IMethodRemoter MethodRemoter
        {
            get { return methodRemoter; }
        }

        /// <summary>
        /// Gets the configured <b>assemblyString</b> of the <see cref="Assembly"/> which contains the <see cref="IMethodRemoter"/> class.
        /// </summary>
        /// <value><see cref="Assembly.FullName"/> of the assembly which contains the remoter. Default is <b>BizElements.BusinessLayer</b>.</value>
        public static string MethodRemoterAssembly
        {
            get { return methodRemoterAssembly; }
        }

        /// <summary>
        /// Gets the configured <b>typeName</b> of the <see cref="IMethodRemoter"/> class.
        /// </summary>
        /// <value><see cref="Type.FullName"/> of the class which implements the <see cref="IMethodRemoter"/>
        /// interface. Default is <b>BizElements.BusinessLayer.DirectMethodCaller</b>.</value>
        public static string MethodRemoterClass
        {
            get { return methodRemoterClass; }
        }

        /// <summary>
        /// Gets the configured URL to which method calls are remoted.
        /// </summary>
        public static string MethodRemoterUrl
        {
            get { return methodRemoterUrl; }
        }

        #endregion
        
        /// <summary>Marshals object to process defined by the remoter, executes the method and returns the changed object and method's return value to source domain.</summary>
        /// <param name="businessObject">Object which is to be marshalled to the defined process and supports the execution 
        /// of the specified method. May be changed in remote domain. Must be marked with 
        /// <see cref="System.SerializableAttribute"/> atribute.</param>
        /// <param name="methodName">Method which is to be executed in the remote domain.</param>
        /// <param name="methodParameters">Method parameters. May not be changed in remote domain. Passed by value.
        /// All parameters must be marked with <see cref="System.SerializableAttribute"/> atribute.</param>
        /// <returns>Method's return value. Must be marked with <see cref="System.SerializableAttribute"/> atribute.</returns>
        public static object InvokeMethod(object businessObject, string methodName, params object[] methodParameters)
        {
            return MethodRemoter.InvokeMethod(businessObject, methodName, methodParameters);
        }

        /// <summary>Marshals object to process defined by the remoter, executes the generic method and returns the changed object and method's return value to source domain.</summary>
        /// <param name="businessObject">Object which is to be marshalled to the defined process and supports the execution 
        /// of the specified method. May be changed in remote domain. Must be marked with 
        /// <see cref="System.SerializableAttribute"/> atribute.</param>
        /// <param name="methodName">Method which is to be executed in the remote domain.</param>
        /// <param name="methodParameters">Method parameters. May not be changed in remote domain. Passed by value.
        /// All parameters must be marked with <see cref="System.SerializableAttribute"/> atribute.</param>
        /// <returns>Method's return value. Must be marked with <see cref="System.SerializableAttribute"/> atribute.</returns>
        public static object InvokeGenericMethod(object businessObject, string methodName, params object[] methodParameters)
        {
            return MethodRemoter.InvokeGenericMethod(businessObject, methodName, methodParameters);
        }

        #region InvokeMethod - Procedures.

        /// <summary>
        /// Remotely invokes a parameterless procedure on the provided object.
        /// </summary>
        /// <param name="businessObject">The object on which to remotely invoke the method.</param>
        /// <param name="method">Method which is to be executed in the remote domain.</param>
        public static void InvokeMethod(object businessObject, Delegates.Procedure method)
        {
            if (MethodRemoter.ImplementsRemoting)
            {
                if (method.Method.IsGenericMethod)
                    MethodRemoter.InvokeGenericMethod(businessObject, method.Method.Name);
                else
                    MethodRemoter.InvokeMethod(businessObject, method.Method.Name);
            }
            else
            {
                method();
            }
        }

        /// <summary>
        /// Remotely invokes a procedure which accepts one parameter on the provided object.
        /// </summary>
        /// <typeparam name="TArg">Type of the method's argument.</typeparam>
        /// <param name="businessObject">The object on which to remotely invoke the method.</param>
        /// <param name="method">Method which is to be executed in the remote domain.</param>
        /// <param name="arg">Method argument. Must be serializable.</param>        
        public static void InvokeMethod<TArg>(object businessObject, Delegates.Procedure<TArg> method, TArg arg)
        {
            if (MethodRemoter.ImplementsRemoting)
            {
                if (method.Method.IsGenericMethod)
                    MethodRemoter.InvokeGenericMethod(businessObject, method.Method.Name, arg);
                else
                    MethodRemoter.InvokeMethod(businessObject, method.Method.Name, arg);
            }
            else
            {
                method(arg);
            }
        }

        /// <summary>
        /// Remotely invokes a procedure which accepts two parameters on the provided object.
        /// </summary>
        /// <typeparam name="TArg1">Type of the method's first argument.</typeparam>
        /// <typeparam name="TArg2">Type of the method's second argument.</typeparam>
        /// <param name="businessObject">The object on which to remotely invoke the method.</param>
        /// <param name="method">Method which is to be executed in the remote domain.</param>
        /// <param name="arg1">First argument. Must be serializable.</param>
        /// <param name="arg2">Second argument. Must be serializable.</param>
        public static void InvokeMethod<TArg1, TArg2>(object businessObject, Delegates.Procedure<TArg1, TArg2> method, TArg1 arg1, TArg2 arg2)
        {
            if (MethodRemoter.ImplementsRemoting)
            {
                if (method.Method.IsGenericMethod)
                    MethodRemoter.InvokeGenericMethod(businessObject, method.Method.Name, arg1, arg2);
                else
                    MethodRemoter.InvokeMethod(businessObject, method.Method.Name, arg1, arg2);
            }
            else
            {
                method(arg1, arg2);
            }
        }

        /// <summary>
        /// Remotely invokes a procedure which accepts three parameters on the provided object.
        /// </summary>
        /// <typeparam name="TArg1">Type of the method's first argument.</typeparam>
        /// <typeparam name="TArg2">Type of the method's second argument.</typeparam>
        /// <typeparam name="TArg3">Type of the method's third argument.</typeparam>
        /// <param name="businessObject">The object on which to remotely invoke the method.</param>
        /// <param name="method">Method which is to be executed in the remote domain.</param>
        /// <param name="arg1">First argument. Must be serializable.</param>
        /// <param name="arg2">Second argument. Must be serializable.</param>
        /// <param name="arg3">Third argument. Must be serializable.</param>
        public static void InvokeMethod<TArg1, TArg2, TArg3>(object businessObject, Delegates.Procedure<TArg1, TArg2, TArg3> method, TArg1 arg1, TArg2 arg2, TArg3 arg3)
        {
            if (MethodRemoter.ImplementsRemoting)
            {
                if (method.Method.IsGenericMethod)
                    MethodRemoter.InvokeGenericMethod(businessObject, method.Method.Name, arg1, arg2, arg3);
                else
                    MethodRemoter.InvokeMethod(businessObject, method.Method.Name, arg1, arg2, arg3);
            }
            else
            {
                method(arg1, arg2, arg3);
            }
        }

        /// <summary>
        /// Remotely invokes a procedure which accepts four parameters on the provided object.
        /// </summary>
        /// <typeparam name="TArg1">Type of the method's first argument.</typeparam>
        /// <typeparam name="TArg2">Type of the method's second argument.</typeparam>
        /// <typeparam name="TArg3">Type of the method's third argument.</typeparam>
        /// <typeparam name="TArg4">Type of the method's fourth argument.</typeparam>
        /// <param name="businessObject">The object on which to remotely invoke the method.</param>
        /// <param name="method">Method which is to be executed in the remote domain.</param>
        /// <param name="arg1">First argument. Must be serializable.</param>
        /// <param name="arg2">Second argument. Must be serializable.</param>
        /// <param name="arg3">Third argument. Must be serializable.</param>
        /// <param name="arg4">Fourth argument. Must be serializable.</param>
        public static void InvokeMethod<TArg1, TArg2, TArg3, TArg4>(object businessObject, Delegates.Procedure<TArg1, TArg2, TArg3, TArg4> method, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4)
        {
            if (MethodRemoter.ImplementsRemoting)
            {
                if (method.Method.IsGenericMethod)
                    MethodRemoter.InvokeGenericMethod(businessObject, method.Method.Name, arg1, arg2, arg3, arg4);
                else
                    MethodRemoter.InvokeMethod(businessObject, method.Method.Name, arg1, arg2, arg3, arg4);
            }
            else
            {
                method(arg1, arg2, arg3, arg4);
            }
        }

        /// <summary>
        /// Remotely invokes a procedure which accepts five parameters on the provided object.
        /// </summary>
        /// <typeparam name="TArg1">Type of the method's first argument.</typeparam>
        /// <typeparam name="TArg2">Type of the method's second argument.</typeparam>
        /// <typeparam name="TArg3">Type of the method's third argument.</typeparam>
        /// <typeparam name="TArg4">Type of the method's fourth argument.</typeparam>
        /// <typeparam name="TArg5">Type of the method's fifth argument.</typeparam>
        /// <param name="businessObject">The object on which to remotely invoke the method.</param>
        /// <param name="method">Method which is to be executed in the remote domain.</param>
        /// <param name="arg1">First argument. Must be serializable.</param>
        /// <param name="arg2">Second argument. Must be serializable.</param>
        /// <param name="arg3">Third argument. Must be serializable.</param>
        /// <param name="arg4">Fourth argument. Must be serializable.</param>
        /// <param name="arg5">Fifth argument. Must be serializable.</param>
        public static void InvokeMethod<TArg1, TArg2, TArg3, TArg4, TArg5>(object businessObject, Delegates.Procedure<TArg1, TArg2, TArg3, TArg4, TArg5> method, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5)
        {
            if (MethodRemoter.ImplementsRemoting)
            {
                if (method.Method.IsGenericMethod)
                    MethodRemoter.InvokeGenericMethod(businessObject, method.Method.Name, arg1, arg2, arg3, arg4, arg5);
                else
                    MethodRemoter.InvokeMethod(businessObject, method.Method.Name, arg1, arg2, arg3, arg4, arg5);
            }
            else
            {
                method(arg1, arg2, arg3, arg4, arg5);
            }
        }

        #endregion

        #region InvokeMethod - Functions.

        /// <summary>
        /// Remotely invokes a parameterless function on the provided object.
        /// </summary>
        /// <typeparam name="TRetVal">Type of the method's return value.</typeparam>
        /// <param name="businessObject">The object on which to remotely invoke the method.</param>
        /// <param name="method">Method which is to be executed in the remote domain.</param>
        /// <returns>A value of the specified type. Must be serializable.</returns>
        public static TRetVal InvokeMethod<TRetVal>(object businessObject, Delegates.Function<TRetVal> method)
        {
            if (MethodRemoter.ImplementsRemoting)
            {
                if (method.Method.IsGenericMethod)
                    return (TRetVal)MethodRemoter.InvokeGenericMethod(businessObject, method.Method.Name);
                else
                    return (TRetVal)MethodRemoter.InvokeMethod(businessObject, method.Method.Name);
            }
            else
            {
                return method();
            }
        }

        /// <summary>
        /// Remotely invokes a function which accepts one parameter on the provided object.
        /// </summary>
        /// <typeparam name="TRetVal">Type of the method's return value.</typeparam>
        /// <typeparam name="TArg">Type of the method's argument.</typeparam>
        /// <param name="businessObject">The object on which to remotely invoke the method.</param>
        /// <param name="method">Method which is to be executed in the remote domain.</param>
        /// <param name="arg">Method argument. Must be serializable.</param>   
        /// <returns>A value of the specified type. Must be serializable.</returns>
        public static TRetVal InvokeMethod<TRetVal, TArg>(object businessObject, Delegates.Function<TRetVal, TArg> method, TArg arg)
        {
            if (MethodRemoter.ImplementsRemoting)
            {
                if (method.Method.IsGenericMethod)
                    return (TRetVal)MethodRemoter.InvokeGenericMethod(businessObject, method.Method.Name, arg);
                else
                    return (TRetVal)MethodRemoter.InvokeMethod(businessObject, method.Method.Name, arg);
            }
            else
            {
                return method(arg);
            }
        }

        /// <summary>
        /// Remotely invokes a function which accepts two parameters on the provided object.
        /// </summary>
        /// <typeparam name="TRetVal">Type of the method's return value.</typeparam>
        /// <typeparam name="TArg1">Type of the method's first argument.</typeparam>
        /// <typeparam name="TArg2">Type of the method's second argument.</typeparam>
        /// <param name="businessObject">The object on which to remotely invoke the method.</param>
        /// <param name="method">Method which is to be executed in the remote domain.</param>
        /// <param name="arg1">First argument. Must be serializable.</param>
        /// <param name="arg2">Second argument. Must be serializable.</param>
        /// <returns>A value of the specified type. Must be serializable.</returns>
        public static TRetVal InvokeMethod<TRetVal, TArg1, TArg2>(object businessObject, Delegates.Function<TRetVal, TArg1, TArg2> method, TArg1 arg1, TArg2 arg2)
        {
            if (MethodRemoter.ImplementsRemoting)
            {
                if (method.Method.IsGenericMethod)
                    return (TRetVal)MethodRemoter.InvokeGenericMethod(businessObject, method.Method.Name, arg1, arg2);
                else
                    return (TRetVal)MethodRemoter.InvokeMethod(businessObject, method.Method.Name, arg1, arg2);
            }
            else
            {
                return method(arg1, arg2);
            }
        }

        /// <summary>
        /// Remotely invokes a function which accepts three parameters on the provided object.
        /// </summary>
        /// <typeparam name="TRetVal">Type of the method's return value.</typeparam>
        /// <typeparam name="TArg1">Type of the method's first argument.</typeparam>
        /// <typeparam name="TArg2">Type of the method's second argument.</typeparam>
        /// <typeparam name="TArg3">Type of the method's third argument.</typeparam>
        /// <param name="businessObject">The object on which to remotely invoke the method.</param>
        /// <param name="method">Method which is to be executed in the remote domain.</param>
        /// <param name="arg1">First argument. Must be serializable.</param>
        /// <param name="arg2">Second argument. Must be serializable.</param>
        /// <param name="arg3">Third argument. Must be serializable.</param>
        /// <returns>A value of the specified type. Must be serializable.</returns>
        public static TRetVal InvokeMethod<TRetVal, TArg1, TArg2, TArg3>(object businessObject, Delegates.Function<TRetVal, TArg1, TArg2, TArg3> method, TArg1 arg1, TArg2 arg2, TArg3 arg3)
        {
            if (MethodRemoter.ImplementsRemoting)
            {
                if (method.Method.IsGenericMethod)
                    return (TRetVal)MethodRemoter.InvokeGenericMethod(businessObject, method.Method.Name, arg1, arg2, arg3);
                else
                    return (TRetVal)MethodRemoter.InvokeMethod(businessObject, method.Method.Name, arg1, arg2, arg3);
            }
            else
            {
                return method(arg1, arg2, arg3);
            }
        }

        /// <summary>
        /// Remotely invokes a function which accepts four parameters on the provided object.
        /// </summary>
        /// <typeparam name="TRetVal">Type of the method's return value.</typeparam>
        /// <typeparam name="TArg1">Type of the method's first argument.</typeparam>
        /// <typeparam name="TArg2">Type of the method's second argument.</typeparam>
        /// <typeparam name="TArg3">Type of the method's third argument.</typeparam>
        /// <typeparam name="TArg4">Type of the method's fourth argument.</typeparam>
        /// <param name="businessObject">The object on which to remotely invoke the method.</param>
        /// <param name="method">Method which is to be executed in the remote domain.</param>
        /// <param name="arg1">First argument. Must be serializable.</param>
        /// <param name="arg2">Second argument. Must be serializable.</param>
        /// <param name="arg3">Third argument. Must be serializable.</param>
        /// <param name="arg4">Fourth argument. Must be serializable.</param>
        /// <returns>A value of the specified type. Must be serializable.</returns>
        public static TRetVal InvokeMethod<TRetVal, TArg1, TArg2, TArg3, TArg4>(object businessObject, Delegates.Function<TRetVal, TArg1, TArg2, TArg3, TArg4> method, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4)
        {
            if (MethodRemoter.ImplementsRemoting)
            {
                if (method.Method.IsGenericMethod)
                    return (TRetVal)MethodRemoter.InvokeGenericMethod(businessObject, method.Method.Name, arg1, arg2, arg3, arg4);
                else
                    return (TRetVal)MethodRemoter.InvokeMethod(businessObject, method.Method.Name, arg1, arg2, arg3, arg4);
            }
            else
            {
                return method(arg1, arg2, arg3, arg4);
            }
        }

        /// <summary>
        /// Remotely invokes a function which accepts five parameters on the provided object.
        /// </summary>
        /// <typeparam name="TRetVal">Type of the method's return value.</typeparam>
        /// <typeparam name="TArg1">Type of the method's first argument.</typeparam>
        /// <typeparam name="TArg2">Type of the method's second argument.</typeparam>
        /// <typeparam name="TArg3">Type of the method's third argument.</typeparam>
        /// <typeparam name="TArg4">Type of the method's fourth argument.</typeparam>
        /// <typeparam name="TArg5">Type of the method's fifth argument.</typeparam>
        /// <param name="businessObject">The object on which to remotely invoke the method.</param>
        /// <param name="method">Method which is to be executed in the remote domain.</param>
        /// <param name="arg1">First argument. Must be serializable.</param>
        /// <param name="arg2">Second argument. Must be serializable.</param>
        /// <param name="arg3">Third argument. Must be serializable.</param>
        /// <param name="arg4">Fourth argument. Must be serializable.</param>
        /// <param name="arg5">Fifth argument. Must be serializable.</param>
        /// <returns>A value of the specified type. Must be serializable.</returns>
        public static TRetVal InvokeMethod<TRetVal, TArg1, TArg2, TArg3, TArg4, TArg5>(object businessObject, Delegates.Function<TRetVal, TArg1, TArg2, TArg3, TArg4, TArg5> method, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5)
        {
            if (MethodRemoter.ImplementsRemoting)
            {
                if (method.Method.IsGenericMethod)
                    return (TRetVal)MethodRemoter.InvokeGenericMethod(businessObject, method.Method.Name, arg1, arg2, arg3, arg4, arg5);
                else
                    return (TRetVal)MethodRemoter.InvokeMethod(businessObject, method.Method.Name, arg1, arg2, arg3, arg4, arg5);
            }
            else
            {
                return method(arg1, arg2, arg3, arg4, arg5);
            }
        }

        #endregion
        
        /// <summary>Marshals method arguments to process defined by the remoter, executes the static method and returns the method's return value to source domain.</summary>
        /// <param name="type">Type which contains the specified static method.</param>
        /// <param name="methodName">Method to be executed.</param>
        /// <param name="methodParameters">Method parameters. May not be changed in remote domain. Passed by value.
        /// All parameters must be marked with <see cref="System.SerializableAttribute"/> atribute.</param>
        /// <returns>Method's return value. Must be marked with <see cref="System.SerializableAttribute"/> atribute.</returns>
        public static object InvokeStaticMethod(Type type, string methodName, params object[] methodParameters)
        {
            return MethodRemoter.InvokeStaticMethod(type, methodName, methodParameters);
        }

        /// <summary>Marshals method arguments to process defined by the remoter, executes the generic static method and returns the method's return value to source domain.</summary>
        /// <param name="type">Type which contains the specified static method.</param>
        /// <param name="methodName">Method to be executed.</param>
        /// <param name="methodParameters">Method parameters. May not be changed in remote domain. Passed by value.
        /// All parameters must be marked with <see cref="System.SerializableAttribute"/> atribute.</param>
        /// <returns>Method's return value. Must be marked with <see cref="System.SerializableAttribute"/> atribute.</returns>
        public static object InvokeGenericStaticMethod(Type type, string methodName, params object[] methodParameters)
        {
            return MethodRemoter.InvokeGenericStaticMethod(type, methodName, methodParameters);
        }

        #region InvokeStaticMethod - Procedures.

        /// <summary>
        /// Remotely invokes a static parameterless procedure.
        /// </summary>
        /// <param name="method">Static method which is to be executed in the remote domain.</param>
        public static void InvokeStaticMethod(Delegates.Procedure method)
        {
            if (MethodRemoter.ImplementsRemoting)
            {
                if (method.Method.IsGenericMethod)
                    MethodRemoter.InvokeGenericStaticMethod(method.Method.DeclaringType, method.Method.Name);
                else
                    MethodRemoter.InvokeStaticMethod(method.Method.DeclaringType, method.Method.Name);
            }
            else
            {
                method();
            }
        }

        /// <summary>
        /// Remotely invokes a static procedure which accepts one parameter.
        /// </summary>
        /// <typeparam name="TArg">Type of the method's argument.</typeparam>
        /// <param name="method">Static method which is to be executed in the remote domain.</param>
        /// <param name="arg">Method argument. Must be serializable.</param>
        public static void InvokeStaticMethod<TArg>(Delegates.Procedure<TArg> method, TArg arg)
        {
            if (MethodRemoter.ImplementsRemoting)
            {
                if (method.Method.IsGenericMethod)
                    MethodRemoter.InvokeGenericStaticMethod(method.Method.DeclaringType, method.Method.Name, arg);
                else
                    MethodRemoter.InvokeStaticMethod(method.Method.DeclaringType, method.Method.Name, arg);
            }
            else
            {
                method(arg);
            }
        }

        /// <summary>
        /// Remotely invokes a static procedure which accepts two parameters.
        /// </summary>
        /// <typeparam name="TArg1">Type of the method's first argument.</typeparam>
        /// <typeparam name="TArg2">Type of the method's second argument.</typeparam>
        /// <param name="method">Static method which is to be executed in the remote domain.</param>
        /// <param name="arg1">First argument. Must be serializable.</param>
        /// <param name="arg2">Second argument. Must be serializable.</param>
        public static void InvokeStaticMethod<TArg1, TArg2>(Delegates.Procedure<TArg1, TArg2> method, TArg1 arg1, TArg2 arg2)
        {
            if (MethodRemoter.ImplementsRemoting)
            {
                if (method.Method.IsGenericMethod)
                    MethodRemoter.InvokeGenericStaticMethod(method.Method.DeclaringType, method.Method.Name, arg1, arg2);
                else
                    MethodRemoter.InvokeStaticMethod(method.Method.DeclaringType, method.Method.Name, arg1, arg2);
            }
            else
            {
                method(arg1, arg2);
            }
        }

        /// <summary>
        /// Remotely invokes a static procedure which accepts three parameters.
        /// </summary>
        /// <typeparam name="TArg1">Type of the method's first argument.</typeparam>
        /// <typeparam name="TArg2">Type of the method's second argument.</typeparam>
        /// <typeparam name="TArg3">Type of the method's third argument.</typeparam>
        /// <param name="method">Static method which is to be executed in the remote domain.</param>
        /// <param name="arg1">First argument. Must be serializable.</param>
        /// <param name="arg2">Second argument. Must be serializable.</param>
        /// <param name="arg3">Third argument. Must be serializable.</param>
        public static void InvokeStaticMethod<TArg1, TArg2, TArg3>(Delegates.Procedure<TArg1, TArg2, TArg3> method, TArg1 arg1, TArg2 arg2, TArg3 arg3)
        {
            if (MethodRemoter.ImplementsRemoting)
            {
                if (method.Method.IsGenericMethod)
                    MethodRemoter.InvokeGenericStaticMethod(method.Method.DeclaringType, method.Method.Name, arg1, arg2, arg3);
                else
                    MethodRemoter.InvokeStaticMethod(method.Method.DeclaringType, method.Method.Name, arg1, arg2, arg3);
            }
            else
            {
                method(arg1, arg2, arg3);
            }
        }

        /// <summary>
        /// Remotely invokes a static procedure which accepts four parameters.
        /// </summary>
        /// <typeparam name="TArg1">Type of the method's first argument.</typeparam>
        /// <typeparam name="TArg2">Type of the method's second argument.</typeparam>
        /// <typeparam name="TArg3">Type of the method's third argument.</typeparam>
        /// <typeparam name="TArg4">Type of the method's fourth argument.</typeparam>
        /// <param name="method">Static method which is to be executed in the remote domain.</param>
        /// <param name="arg1">First argument. Must be serializable.</param>
        /// <param name="arg2">Second argument. Must be serializable.</param>
        /// <param name="arg3">Third argument. Must be serializable.</param>
        /// <param name="arg4">Fourth argument. Must be serializable.</param>
        public static void InvokeStaticMethod<TArg1, TArg2, TArg3, TArg4>(Delegates.Procedure<TArg1, TArg2, TArg3, TArg4> method, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4)
        {
            if (MethodRemoter.ImplementsRemoting)
            {
                if (method.Method.IsGenericMethod)
                    MethodRemoter.InvokeGenericStaticMethod(method.Method.DeclaringType, method.Method.Name, arg1, arg2, arg3, arg4);
                else
                    MethodRemoter.InvokeStaticMethod(method.Method.DeclaringType, method.Method.Name, arg1, arg2, arg3, arg4);
            }
            else
            {
                method(arg1, arg2, arg3, arg4);
            }
        }

        /// <summary>
        /// Remotely invokes a static procedure which accepts five parameters.
        /// </summary>
        /// <typeparam name="TArg1">Type of the method's first argument.</typeparam>
        /// <typeparam name="TArg2">Type of the method's second argument.</typeparam>
        /// <typeparam name="TArg3">Type of the method's third argument.</typeparam>
        /// <typeparam name="TArg4">Type of the method's fourth argument.</typeparam>
        /// <typeparam name="TArg5">Type of the method's fifth argument.</typeparam>
        /// <param name="method">Static method which is to be executed in the remote domain.</param>
        /// <param name="arg1">First argument. Must be serializable.</param>
        /// <param name="arg2">Second argument. Must be serializable.</param>
        /// <param name="arg3">Third argument. Must be serializable.</param>
        /// <param name="arg4">Fourth argument. Must be serializable.</param>
        /// <param name="arg5">Fifth argument. Must be serializable.</param>
        public static void InvokeStaticMethod<TArg1, TArg2, TArg3, TArg4, TArg5>(Delegates.Procedure<TArg1, TArg2, TArg3, TArg4, TArg5> method, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5)
        {
            if (MethodRemoter.ImplementsRemoting)
            {
                if (method.Method.IsGenericMethod)
                    MethodRemoter.InvokeGenericStaticMethod(method.Method.DeclaringType, method.Method.Name, arg1, arg2, arg3, arg4, arg5);
                else
                    MethodRemoter.InvokeStaticMethod(method.Method.DeclaringType, method.Method.Name, arg1, arg2, arg3, arg4, arg5);
            }
            else
            {
                method(arg1, arg2, arg3, arg4, arg5);
            }
        }

        #endregion

        #region InvokeStaticMethod - Functions.

        /// <summary>
        /// Remotely invokes a static parameterless function.
        /// </summary>
        /// <typeparam name="TRetVal">Type of the method's return value.</typeparam>
        /// <param name="method">Static method which is to be executed in the remote domain.</param>
        /// <returns>A value of the specified type. Must be serializable.</returns>
        public static TRetVal InvokeStaticMethod<TRetVal>(Delegates.Function<TRetVal> method)
        {
            if (MethodRemoter.ImplementsRemoting)
            {
                if (method.Method.IsGenericMethod)
                    return (TRetVal)MethodRemoter.InvokeGenericStaticMethod(method.Method.DeclaringType, method.Method.Name);
                else
                    return (TRetVal)MethodRemoter.InvokeStaticMethod(method.Method.DeclaringType, method.Method.Name);
            }
            else
            {
                return method();
            }
        }

        /// <summary>
        /// Remotely invokes a static function which accepts one parameter.
        /// </summary>
        /// <typeparam name="TRetVal">Type of the method's return value.</typeparam>
        /// <typeparam name="TArg">Type of the method's argument.</typeparam>
        /// <param name="method">Static method which is to be executed in the remote domain.</param>
        /// <param name="arg">Method argument. Must be serializable.</param>
        /// <returns>A value of the specified type. Must be serializable.</returns>
        public static TRetVal InvokeStaticMethod<TRetVal, TArg>(Delegates.Function<TRetVal, TArg> method, TArg arg)
        {
            if (MethodRemoter.ImplementsRemoting)
            {
                if (method.Method.IsGenericMethod)
                    return (TRetVal)MethodRemoter.InvokeGenericStaticMethod(method.Method.DeclaringType, method.Method.Name, arg);
                else
                    return (TRetVal)MethodRemoter.InvokeStaticMethod(method.Method.DeclaringType, method.Method.Name, arg);
            }
            else
            {
                return method(arg);
            }
        }

        /// <summary>
        /// Remotely invokes a static function which accepts two parameters.
        /// </summary>
        /// <typeparam name="TRetVal">Type of the method's return value.</typeparam>
        /// <typeparam name="TArg1">Type of the method's first argument.</typeparam>
        /// <typeparam name="TArg2">Type of the method's second argument.</typeparam>
        /// <param name="method">Static method which is to be executed in the remote domain.</param>
        /// <param name="arg1">First argument. Must be serializable.</param>
        /// <param name="arg2">Second argument. Must be serializable.</param>
        /// <returns>A value of the specified type. Must be serializable.</returns>
        public static TRetVal InvokeStaticMethod<TRetVal, TArg1, TArg2>(Delegates.Function<TRetVal, TArg1, TArg2> method, TArg1 arg1, TArg2 arg2)
        {
            if (MethodRemoter.ImplementsRemoting)
            {
                if (method.Method.IsGenericMethod)
                    return (TRetVal)MethodRemoter.InvokeGenericStaticMethod(method.Method.DeclaringType, method.Method.Name, arg1, arg2);
                else
                    return (TRetVal)MethodRemoter.InvokeStaticMethod(method.Method.DeclaringType, method.Method.Name, arg1, arg2);
            }
            else
            {
                return method(arg1, arg2);
            }
        }

        /// <summary>
        /// Remotely invokes a static function which accepts three parameters.
        /// </summary>
        /// <typeparam name="TRetVal">Type of the method's return value.</typeparam>
        /// <typeparam name="TArg1">Type of the method's first argument.</typeparam>
        /// <typeparam name="TArg2">Type of the method's second argument.</typeparam>
        /// <typeparam name="TArg3">Type of the method's third argument.</typeparam>
        /// <param name="method">Static method which is to be executed in the remote domain.</param>
        /// <param name="arg1">First argument. Must be serializable.</param>
        /// <param name="arg2">Second argument. Must be serializable.</param>
        /// <param name="arg3">Third argument. Must be serializable.</param>
        /// <returns>A value of the specified type. Must be serializable.</returns>
        public static TRetVal InvokeStaticMethod<TRetVal, TArg1, TArg2, TArg3>(Delegates.Function<TRetVal, TArg1, TArg2, TArg3> method, TArg1 arg1, TArg2 arg2, TArg3 arg3)
        {
            if (MethodRemoter.ImplementsRemoting)
            {
                if (method.Method.IsGenericMethod)
                    return (TRetVal)MethodRemoter.InvokeGenericStaticMethod(method.Method.DeclaringType, method.Method.Name, arg1, arg2, arg3);
                else
                    return (TRetVal)MethodRemoter.InvokeStaticMethod(method.Method.DeclaringType, method.Method.Name, arg1, arg2, arg3);
            }
            else
            {
                return method(arg1, arg2, arg3);
            }
        }

        /// <summary>
        /// Remotely invokes a static function which accepts four parameters.
        /// </summary>
        /// <typeparam name="TRetVal">Type of the method's return value.</typeparam>
        /// <typeparam name="TArg1">Type of the method's first argument.</typeparam>
        /// <typeparam name="TArg2">Type of the method's second argument.</typeparam>
        /// <typeparam name="TArg3">Type of the method's third argument.</typeparam>
        /// <typeparam name="TArg4">Type of the method's fourth argument.</typeparam>
        /// <param name="method">Static method which is to be executed in the remote domain.</param>
        /// <param name="arg1">First argument. Must be serializable.</param>
        /// <param name="arg2">Second argument. Must be serializable.</param>
        /// <param name="arg3">Third argument. Must be serializable.</param>
        /// <param name="arg4">Fourth argument. Must be serializable.</param>
        /// <returns>A value of the specified type. Must be serializable.</returns>
        public static TRetVal InvokeStaticMethod<TRetVal, TArg1, TArg2, TArg3, TArg4>(Delegates.Function<TRetVal, TArg1, TArg2, TArg3, TArg4> method, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4)
        {
            if (MethodRemoter.ImplementsRemoting)
            {
                if (method.Method.IsGenericMethod)
                    return (TRetVal)MethodRemoter.InvokeGenericStaticMethod(method.Method.DeclaringType, method.Method.Name, arg1, arg2, arg3, arg4);
                else
                    return (TRetVal)MethodRemoter.InvokeStaticMethod(method.Method.DeclaringType, method.Method.Name, arg1, arg2, arg3, arg4);
            }
            else
            {
                return method(arg1, arg2, arg3, arg4);
            }
        }

        /// <summary>
        /// Remotely invokes a static function which accepts five parameters.
        /// </summary>
        /// <typeparam name="TRetVal">Type of the method's return value.</typeparam>
        /// <typeparam name="TArg1">Type of the method's first argument.</typeparam>
        /// <typeparam name="TArg2">Type of the method's second argument.</typeparam>
        /// <typeparam name="TArg3">Type of the method's third argument.</typeparam>
        /// <typeparam name="TArg4">Type of the method's fourth argument.</typeparam>
        /// <typeparam name="TArg5">Type of the method's fifth argument.</typeparam>
        /// <param name="method">Static method which is to be executed in the remote domain.</param>
        /// <param name="arg1">First argument. Must be serializable.</param>
        /// <param name="arg2">Second argument. Must be serializable.</param>
        /// <param name="arg3">Third argument. Must be serializable.</param>
        /// <param name="arg4">Fourth argument. Must be serializable.</param>
        /// <param name="arg5">Fifth argument. Must be serializable.</param>
        /// <returns>A value of the specified type. Must be serializable.</returns>
        public static TRetVal InvokeStaticMethod<TRetVal, TArg1, TArg2, TArg3, TArg4, TArg5>(Delegates.Function<TRetVal, TArg1, TArg2, TArg3, TArg4, TArg5> method, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, TArg5 arg5)
        {
            if (MethodRemoter.ImplementsRemoting)
            {
                if (method.Method.IsGenericMethod)
                    return (TRetVal)MethodRemoter.InvokeGenericStaticMethod(method.Method.DeclaringType, method.Method.Name, arg1, arg2, arg3, arg4, arg5);
                else
                    return (TRetVal)MethodRemoter.InvokeStaticMethod(method.Method.DeclaringType, method.Method.Name, arg1, arg2, arg3, arg4, arg5);
            }
            else
            {
                return method(arg1, arg2, arg3, arg4, arg5);
            }
        }

        #endregion
    }
}
